#define SIMPLEST

using System;
using ChessKit.Common;

namespace ChessKit.ChessLogics
{
  internal struct CompactBoard
  {
    public const int A1 = 0 * 8 + 0;
    public const int A2 = 1 * 8 + 0;
    public const int A3 = 2 * 8 + 0;
    public const int A4 = 3 * 8 + 0;
    public const int A5 = 4 * 8 + 0;
    public const int A6 = 5 * 8 + 0;
    public const int A7 = 6 * 8 + 0;
    public const int A8 = 7 * 8 + 0;

    public const int H1 = 0 * 8 + 7;
    public const int H8 = 7 * 8 + 7;

    public const int E1 = 0 * 8 + 4;
    public const int E8 = 7 * 8 + 4;

    public const int C1 = 0 * 8 + 2;
    public const int C8 = 7 * 8 + 2;
    public const int G8 = 7 * 8 + 6;
    public const int G1 = 0 * 8 + 6;

    public const int D1 = 0 * 8 + 3;
    public const int B1 = 0 * 8 + 1;
    public const int B8 = 7 * 8 + 1;
    public const int D8 = 7 * 8 + 3;
    public const int F8 = 7 * 8 + 5;
    public const int F1 = 0 * 8 + 5;

#if SIMPLEST
    private const int BytesCount = 32;
#elif X88
    private const int BytesCount = 128;
#endif

    private readonly byte[] _bytes;

    /// <summary>Deep copy</summary>
    public CompactBoard(CompactBoard source)
      : this()
    {
      _bytes = new byte[BytesCount];
      Buffer.BlockCopy(source._bytes, 0, _bytes, 0, BytesCount);
    }

    /// <summary>Empty board</summary>
    /// <param name="stub">is ignored</param>
    public CompactBoard(int stub)
      : this()
    {
      _bytes = new byte[BytesCount];
    }

    public CompactPiece this[int compactPosition]
    {
      get
      {
#if SIMPLEST
        switch (compactPosition % 2)
        {
          case 0:
            return (CompactPiece)(_bytes[compactPosition / 2] >> 4);
          case 1:
            return (CompactPiece)(_bytes[compactPosition / 2] & 0xF);
        }
        throw new InvalidOperationException("Cannot get here!");
#elif X88
        return (CompactPiece)_bytes[compactPosition];
#endif
      }
      set
      {
#if SIMPLEST
        var i = compactPosition / 2;
        switch (compactPosition % 2)
        {
          case 0:
            _bytes[i] = (byte)((_bytes[i] & 0xF) | (int)value << 4);
            break;
          case 1:
            _bytes[i] = (byte)((_bytes[i] & 0xF0) | (int)value);
            break;
        }
#elif X88
        _bytes[compactPosition] = (byte)value;
#endif
      }
    }

    public override int GetHashCode()
    {
      unchecked
      {
        int res = _bytes[0];
        for (var i = 1; i < BytesCount; i++)
          res = res * 257 ^ _bytes[i];
        return res;
      }
    }

    public SlideMoveValidation IsValidRookMove(int from, int to)
    {
      if (Abs(from - to) > 7)
      {
        // vertical move
        if (from % 8 != to % 8)
        {
          return SlideMoveValidation.PieceDoesntMoveThisWay;
        }

        if (from < to)
        {
          for (var i = from + 8; i < to; i += 8)
            if (this[i] != CompactPiece.EmptyCell)
              return SlideMoveValidation.JumpRequired;
        }
        else
        {
          for (var i = from - 8; i > to; i -= 8)
            if (this[i] != CompactPiece.EmptyCell)
              return SlideMoveValidation.JumpRequired;
        }
        return SlideMoveValidation.Valid;
      }
      // horizontal move
      if (from / 8 != to / 8)
      {
        return SlideMoveValidation.PieceDoesntMoveThisWay;
      }

      if (from < to)
      {
        for (var i = from + 1; i < to; i++)
          if (this[i] != CompactPiece.EmptyCell)
            return SlideMoveValidation.JumpRequired;
      }
      else
      {
        for (var i = from - 1; i > to; i--)
          if (this[i] != CompactPiece.EmptyCell)
            return SlideMoveValidation.JumpRequired;
      }
      return SlideMoveValidation.Valid;
    }
    public SlideMoveValidation IsValidBishopMove(int from, int to)
    {
      var dist = Abs(from - to);
      if (dist % 9 == 0)
      {
        if (from < to)
        {
          if (from % 8 >= to % 8 || from / 8 >= to / 8)
            return SlideMoveValidation.PieceDoesntMoveThisWay;

          for (var i = from + 9; i < to; i += 9)
            if (this[i] != CompactPiece.EmptyCell)
              return SlideMoveValidation.JumpRequired;
        }
        else
        {
          if (from % 8 <= to % 8 || from / 8 <= to / 8)
            return SlideMoveValidation.PieceDoesntMoveThisWay;

          for (var i = from - 9; i > to; i -= 9)
            if (this[i] != CompactPiece.EmptyCell)
              return SlideMoveValidation.JumpRequired;
        }

        return SlideMoveValidation.Valid;
      }
      if (dist % 7 == 0)
      {
        if (from < to)
        {
          if (from % 8 <= to % 8 || from / 8 >= to / 8)
            return SlideMoveValidation.PieceDoesntMoveThisWay;

          for (var i = from + 7; i < to; i += 7)
            if (this[i] != CompactPiece.EmptyCell)
              return SlideMoveValidation.JumpRequired;
        }
        else
        {
          if (from % 8 >= to % 8 || from / 8 <= to / 8)
            return SlideMoveValidation.PieceDoesntMoveThisWay;

          for (var i = from - 7; i > to; i -= 7)
            if (this[i] != CompactPiece.EmptyCell)
              return SlideMoveValidation.JumpRequired;
        }

        return SlideMoveValidation.Valid;
      }
      return SlideMoveValidation.PieceDoesntMoveThisWay;
    }
    public bool IsValidKnightMove(int from, int to)
    {
      switch (from - to)
      {
        case -17: return from % 8 < to % 8 || from / 8 < to / 8;//00
        case -15: return from % 8 > to % 8 || from / 8 > to / 8;//11
        case -10: return from % 8 < to % 8 || from / 8 > to / 8;//01
        case -06: return from % 8 > to % 8 || from / 8 > to / 8;//11
        case +17: return from % 8 > to % 8 || from / 8 < to / 8;//10
        case +10: return from % 8 > to % 8 || from / 8 < to / 8;//10
        case +15: return from % 8 < to % 8 || from / 8 < to / 8;//00
        case +06: return from % 8 < to % 8 || from / 8 < to / 8;//00
        default: return false;
      }
    }
    public KingMoveValidation IsValidKingMove(int from, int to)
    {
      switch (from - to)
      {
        case -7: case +9:
          if (from % 8 <= to % 8) return KingMoveValidation.DoesntMoveThisWay;
          return IsUnderAttack(to) 
            ? KingMoveValidation.CantMoveToCellUnderAttack 
            : KingMoveValidation.Move;
        case -1: case +1: 
          if (from / 8 != to / 8) return KingMoveValidation.DoesntMoveThisWay;
          return IsUnderAttack(to) 
            ? KingMoveValidation.CantMoveToCellUnderAttack 
            : KingMoveValidation.Move;
        case -9: case +7: 
          if (from % 8 >= to % 8) return KingMoveValidation.DoesntMoveThisWay;
          return IsUnderAttack(to) 
            ? KingMoveValidation.CantMoveToCellUnderAttack 
            : KingMoveValidation.Move;
        case -8: case +8: 
          return IsUnderAttack(to)  
            ? KingMoveValidation.CantMoveToCellUnderAttack 
            : KingMoveValidation.Move;
        case +2:
          if (from == E1)
          {
            if (this[D1] != 0 || this[B1] != 0) 
              return KingMoveValidation.DoesntMoveThisWay;
            if (IsUnderAttack(D1) || IsUnderAttack(B1))
              return KingMoveValidation.CantCastleThroughCellUnderAttack;
            return KingMoveValidation.WhiteQueenCastling;
          }
          if (from == E8)
          {
            if (this[D8] != 0 || this[B8] != 0) 
              return KingMoveValidation.DoesntMoveThisWay;
            if (IsUnderAttack(D8) || IsUnderAttack(B8))
              return KingMoveValidation.CantCastleThroughCellUnderAttack;
            return KingMoveValidation.BlackQueenCastling;
          }
          return KingMoveValidation.DoesntMoveThisWay;
        case -2:
          if (from == E1)
          {
            if (this[F1] != 0) return KingMoveValidation.DoesntMoveThisWay;
            return IsUnderAttack(F1) 
              ? KingMoveValidation.CantCastleThroughCellUnderAttack
              : KingMoveValidation.WhiteKingCastling;
          }
          if (from == E8)
          {
            if (this[F8] != 0) return KingMoveValidation.DoesntMoveThisWay;
            return IsUnderAttack(F8) 
              ? KingMoveValidation.CantCastleThroughCellUnderAttack 
              : KingMoveValidation.BlackKingCastling;
          }
          return KingMoveValidation.DoesntMoveThisWay;
        default:
          return KingMoveValidation.DoesntMoveThisWay;
      }
    }
    public PawnMoveValidation IsValidPawnMove(PieceColor color, int from, int to)
    {
      if (color == PieceColor.White)
        switch (to - from)
        {
          case 16:
            if (from / 8 != 1) return PawnMoveValidation.DoesntMoveThisWay;
            if (this[to] != CompactPiece.EmptyCell)
              return PawnMoveValidation.DoesntTakeThisWay;
            return this[from + 8] != CompactPiece.EmptyCell
              ? PawnMoveValidation.JumpRequired
              : PawnMoveValidation.DoubleMove;
          case 8:
            if (this[to] != CompactPiece.EmptyCell)
              return PawnMoveValidation.DoesntTakeThisWay;
            return to / 8 == 7 ? PawnMoveValidation.Promotion : PawnMoveValidation.Move;
          case 9: case 7:
            if (to / 8 != from / 8 + 1) return PawnMoveValidation.DoesntMoveThisWay;
            if (this[to] == CompactPiece.EmptyCell) return to / 8 == 5 ?
              PawnMoveValidation.EnPassant : PawnMoveValidation.OnlyTakesThisWay;
            return to / 8 != 7 ? PawnMoveValidation.Take :
              PawnMoveValidation.Promotion | PawnMoveValidation.Take;
          default:
            return PawnMoveValidation.DoesntMoveThisWay;
        }
      switch (from - to)
      {
        case 16:
          if (from / 8 != 6) return PawnMoveValidation.DoesntMoveThisWay;
          if (this[to] != CompactPiece.EmptyCell)
            return PawnMoveValidation.DoesntTakeThisWay;
          return this[from - 8] != CompactPiece.EmptyCell
            ? PawnMoveValidation.JumpRequired
            : PawnMoveValidation.DoubleMove;
        case 8:
          if (this[to] != CompactPiece.EmptyCell)
            return PawnMoveValidation.DoesntTakeThisWay;
          return to / 8 == 0 ? PawnMoveValidation.Promotion : PawnMoveValidation.Move;
        case 9: case 7:
          if (to / 8 != from / 8 - 1) return PawnMoveValidation.DoesntMoveThisWay;
          if (this[to] == CompactPiece.EmptyCell) return to / 8 == 2 ?
            PawnMoveValidation.EnPassant : PawnMoveValidation.OnlyTakesThisWay;
          return to / 8 != 0 ? PawnMoveValidation.Take :
            PawnMoveValidation.Promotion | PawnMoveValidation.Take;
        default:
          return PawnMoveValidation.DoesntMoveThisWay;
      }
    }

    public static int Abs(int value)
    {
      return value < 0 ? -value : value;
    }

    public bool IsUnderAttack(int cell)
    {

      return false;
    }
  }
}